home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MACD 5
/
MACD 5.bin
/
workbench
/
libs
/
intuisup.lha
/
Intuisup
/
source.lha
/
Editor
/
load.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-10-05
|
19KB
|
884 lines
/* $Revision Header *** Header built automatically - do not edit! ***********
*
* (C) Copyright 1991 by Torsten Jürgeleit
*
* Name .....: load.c
* Created ..: Sunday 22-Dec-91 21:22:44
* Revision .: 2
*
* Date Author Comment
* ========= ==================== ====================
* 02-Oct-92 Michael Bjerking New realese, better Screen/Window editor
* 31-Dec-91 Torsten Jürgeleit new font management
* 22-Dec-91 Torsten Jürgeleit Created this file!
*
****************************************************************************
*
* Load template files
*
* $Revision Header ********************************************************/
/* Includes */
#include "includes.h"
#include "defines.h"
#include "imports.h"
#include "protos.h"
/* Defines */
#define LOAD_FILE_READ_BUFFER_SIZE 2000
#define LOAD_FILE_LINE_BUFFER_SIZE 80
#define LOAD_FILE_FLAGS (TEXT_FILE_FLAG_TRIM_LINE | TEXT_FILE_FLAG_SKIP_COMMENTS | TEXT_FILE_FLAG_SKIP_EMPTY_LINES | TEXT_FILE_FLAG_LINE_CONTINUATION)
#define BLOCK_TYPE_NONE 0
#define BLOCK_TYPE_HEADER 1
#define BLOCK_TYPE_FONT 2
#define BLOCK_TYPE_TEMPLATE 3
#define BLOCK_TYPE_BOX 4
#define BLOCK_TYPE_TEXTLIST 5
#define BLOCK_TYPE_BORDERDATA 6
#define BLOCK_TYPE_TEXTDATA 7
#define BLOCK_TYPE_GADGETDATA 8
#define HEADER_ITEM_TYPE_LEFTEDGE 1
#define HEADER_ITEM_TYPE_TOPEDGE 2
#define HEADER_ITEM_TYPE_WIDTH 3
#define HEADER_ITEM_TYPE_HEIGHT 4
#define HEADER_ITEM_TYPE_MINW 5
#define HEADER_ITEM_TYPE_MINH 6
#define HEADER_ITEM_TYPE_MAXW 7
#define HEADER_ITEM_TYPE_MAXH 8
#define HEADER_ITEM_TYPE_DETAILPEN 9
#define HEADER_ITEM_TYPE_BLOCKPEN 10
#define HEADER_ITEM_TYPE_FLAGS 11
#define HEADER_ITEM_TYPE_WINFLAGS 12
#define HEADER_ITEM_TYPE_IDCMPFLAGS 13
#define HEADER_ITEM_TYPE_ID 14
#define HEADER_ITEM_TYPE_NAME 15
#define FONT_ITEM_TYPE_NAME 1
#define FONT_ITEM_TYPE_YSIZE 2
#define TEMPLATE_ITEM_TYPE_NAME 1
#define TEMPLATE_ITEM_TYPE_TYPE 2
#define TEMPLATE_ITEM_TYPE_FLAGS 3
#define BOX_ITEM_TYPE_X1 1
#define BOX_ITEM_TYPE_Y1 2
#define BOX_ITEM_TYPE_X2 3
#define BOX_ITEM_TYPE_Y2 4
#define TEXTLIST_ITEM_TYPE_TEXT 1
#define BORDERDATA_ITEM_TYPE_TYPE 1
#define TEXTDATA_ITEM_TYPE_TYPE 1
#define TEXTDATA_ITEM_TYPE_FLAGS 2
#define TEXTDATA_ITEM_TYPE_TEXT 3
#define TEXTDATA_ITEM_TYPE_TEXTATTR 4
#define GADGETDATA_ITEM_TYPE_TYPE 1
#define GADGETDATA_ITEM_TYPE_FLAGS 2
#define GADGETDATA_ITEM_TYPE_TEXT 3
#define GADGETDATA_ITEM_TYPE_TEXTATTR 4
#define GADGETDATA_ITEM_TYPE_SPECIAL1 5
#define GADGETDATA_ITEM_TYPE_SPECIAL2 6
#define GADGETDATA_ITEM_TYPE_SPECIAL3 7
/* Statics */
STATIC struct TemplateList new_template_list;
STATIC struct TemplateFont *new_font;
STATIC struct Template *new_template;
STATIC BYTE *block_keywords[]=
{
"PROJECTHEADER",
"FONT",
"TEMPLATE",
"BOX",
"TEXTLIST",
"BORDERDATA",
"TEXTDATA",
"GADGETDATA",
NULL
};
STATIC BYTE *header_keywords[]=
{
"LEFTEDGE",
"TOPEDGE",
"WIDTH",
"HEIGHT",
"MINW",
"MINH",
"MAXW",
"MAXH",
"DETAILPEN",
"BLOCKPEN",
"FLAGS",
"WINFLAGS",
"IDCMPFLAGS",
"ID",
"NAME",
NULL
};
STATIC BYTE *font_keywords[]=
{
"NAME",
"YSIZE",
NULL
};
STATIC BYTE *template_keywords[]=
{
"NAME",
"TYPE",
"FLAGS",
NULL
};
STATIC BYTE *box_keywords[]=
{
"X1",
"Y1",
"X2",
"Y2",
NULL
};
STATIC BYTE *textlist_keywords[]=
{
"TEXT",
NULL
};
STATIC BYTE *borderdata_keywords[]=
{
"TYPE",
NULL
};
STATIC BYTE *textdata_keywords[]=
{
"TYPE",
"FLAGS",
"TEXT",
"TEXTATTR",
NULL
};
STATIC BYTE *gadgetdata_keywords[]=
{
"TYPE",
"FLAGS",
"TEXT",
"TEXTATTR",
"SPECIAL1",
"SPECIAL2",
"SPECIAL3",
NULL
};
/* Load project */
SHORT
load_project(USHORT mode)
{
struct rtFileRequester *freq = project_file_requester;
SHORT status = EDITOR_STATUS_NORMAL;
BYTE filename[256], buf[256]; /* <- should define a constant */
BYTE text[256];
/* Display ReqTools file requester and check if user selected cancel */
IChangeMousePointer(ewin, NULL, FALSE);
if (mode == LOAD_MODE_NORMAL)
{
strcpy(text, PROJECT_LOAD_HAIL_TEXT);
}
else
{
strcpy(text, PROJECT_APPEND_HAIL_TEXT);
}
if (rtFileRequest(freq, filename, text, TAG_END))
{
struct TemplateList *new_tl = &new_template_list;
struct FileData *fd;
SHORT len = strlen(filename) - 4;
/* Prepare file name and project name */
if (len < 1 || strcmp(filename + len, ".tpl"))
{
strcat(filename, ".tpl");
len += 4;
}
/* path + filename */
if (strlen(freq->Dir) > 0)
{
strcpy(buf, filename);
if (strncmp(buf + strlen(buf) - 1, ":", (size_t) 1) == 0)
sprintf(filename, "%s/%s", freq->Dir, buf);
else
sprintf(filename, "%s%s", freq->Dir, buf);
}
/* Open file */
if (!(fd = IOpenTextFile(filename, LOAD_FILE_READ_BUFFER_SIZE,
LOAD_FILE_LINE_BUFFER_SIZE, LOAD_FILE_FLAGS)))
{
status = EDITOR_ERROR_OPEN_FAILED;
}
else
{
/* Init new template list */
NewList((struct List *) & new_tl->tl_Fonts);
NewList((struct List *) & new_tl->tl_Templates);
new_tl->tl_BorderTemplates = 0;
new_tl->tl_TextTemplates = 0;
new_tl->tl_GadgetTemplates = 0;
new_tl->tl_Flags = DEFAULT_TEMPLATE_LIST_FLAGS;
/* Read all templates */
while ((status = parse_block(fd, new_tl, BLOCK_TYPE_NONE,
mode)) == EDITOR_STATUS_NORMAL);
/* Install and display new template list */
if (status == EDITOR_STATUS_EOF)
{
struct TemplateList *tl = &template_list;
struct TemplateFont *tf;
struct Template *tp;
struct List *list;
/* First set normal status */
status = EDITOR_STATUS_NORMAL;
/* Remove unused template fonts from new template list */
tf = get_head((struct List *) & new_tl->tl_Fonts);
while (tf)
{
struct TemplateFont *next_tf = get_succ((struct Node *)
& tf->tf_MinNode);
if (!tf->tf_UseCount)
{
Remove((struct Node *) & tf->tf_MinNode);
free_template_font(tf);
}
tf = next_tf;
}
if (mode == LOAD_MODE_NORMAL)
{
/* Overwrite old template list */
if ((status = new_project(tl, new_tl->tl_Flags)) ==
EDITOR_STATUS_NORMAL)
{
/* Copy template fonts from new to original template list */
list = (struct List *) & new_tl->tl_Fonts;
while (tf = (struct TemplateFont *) RemHead(list))
{
AddTail((struct List *) & tl->tl_Fonts,
(struct Node *) & tf->tf_MinNode);
}
/* Copy templates from new to original template list */
list = (struct List *) & new_tl->tl_Templates;
while (tp = (struct Template *) RemHead(list))
{
add_template_to_list(tl, tp, FALSE);
display_template(tp);
}
/* Copy template list data */
tl->tl_Flags = new_tl->tl_Flags &
~TEMPLATE_LIST_FLAG_CHANGED;
strcpy(&tl->tl_ProjectID[0], &new_tl->tl_ProjectID[0]);
change_project_name(tl, filename, len);
}
}
else
{
/* Append new template list to old one */
list = (struct List *) & new_tl->tl_Templates;
while (tp = (struct Template *) RemHead(list))
{
struct TextAttr *old_ta, *new_ta;
/* Open template font in original template list and close it in new one */
switch (TEMPLATE_GROUP(tp))
{
case TEMPLATE_GROUP_TEXT:
old_ta = tp->tp_Data.tp_TextData.td_TextAttr;
if (!(new_ta = open_template_font_by_attributes(tl,
(BYTE *) old_ta->ta_Name, old_ta->ta_YSize)))
{
status = EDITOR_ERROR_INVALID_FONT;
}
else
{
tp->tp_Data.tp_TextData.td_TextAttr = new_ta;
close_template_font(new_tl, old_ta);
}
break;
case TEMPLATE_GROUP_GADGET:
old_ta = tp->tp_Data.tp_GadgetData.gd_TextAttr;
if (!(new_ta = open_template_font_by_attributes(tl,
(BYTE *) old_ta->ta_Name, old_ta->ta_YSize)))
{
status = EDITOR_ERROR_INVALID_FONT;
}
else
{
tp->tp_Data.tp_GadgetData.gd_TextAttr = new_ta;
close_template_font(new_tl, old_ta);
}
break;
}
if (status != EDITOR_STATUS_NORMAL)
{
free_template(new_tl, tp);
}
else
{
/* Add template to original template list */
if (tp->tp_Flags & TEMPLATE_FLAG_DEFAULT_NAME)
{
add_template_to_list(tl, tp, TRUE);
}
else
{
add_template_to_list(tl, tp, FALSE);
}
display_template(tp);
tl->tl_Flags |= TEMPLATE_LIST_FLAG_CHANGED;
}
}
}
if (status == EDITOR_STATUS_NORMAL)
{
ISetGadgetAttributes(egl, EDITOR_GADGET_TEMPLATES, 0L, 0L,
USE_CURRENT_VALUE, USE_CURRENT_VALUE, &tl->tl_Templates);
}
}
ICloseTextFile(fd);
/* If error then free incomplete loaded template list */
if (status != EDITOR_STATUS_NORMAL)
{
free_template_list(new_tl);
}
}
}
IRestoreMousePointer(ewin);
if (status != EDITOR_STATUS_NORMAL)
{
show_error(status);
}
return (status);
}
/* Parse block of load file */
STATIC SHORT
parse_block(struct FileData * fd, struct TemplateList * tl,
USHORT block_type, USHORT mode)
{
SHORT status = EDITOR_STATUS_NORMAL;
if (block_type == BLOCK_TYPE_FONT)
{
struct TemplateFont *tf;
if (!(tf = new_font = AllocMem((LONG) sizeof(struct TemplateFont),
(LONG) MEMF_PUBLIC | MEMF_CLEAR)))
{
status = EDITOR_ERROR_OUT_OF_MEM;
}
else
{
struct TextAttr *ta = &tf->tf_TextAttr;
/* Init new template font */
ta->ta_Style = FS_NORMAL;
ta->ta_Flags = FPF_ROMFONT;
tf->tf_UseCount = 0;
}
}
else
{
if (block_type == BLOCK_TYPE_TEMPLATE)
{
struct Template *tp;
if (!(tp = new_template = AllocMem((LONG) sizeof(struct Template),
(LONG) MEMF_PUBLIC | MEMF_CLEAR)))
{
status = EDITOR_ERROR_OUT_OF_MEM;
}
else
{
/* Init new template */
NewList(&tp->tp_TextList);
tp->tp_Node.ln_Name = &tp->tp_TemplateName[0];
}
}
}
if (status == EDITOR_STATUS_NORMAL)
{
/* Block loop */
do
{
BYTE c, *keyword, *arg;
switch (IReadTextLine(fd))
{
case TEXT_FILE_STATUS_NORMAL:
/* Mark end of keyword */
keyword = arg = fd->fd_Line;
while ((c = *arg++) != ' ' && c != '=' && c != '\0');
if (c == '\0')
{
status = EDITOR_ERROR_NO_ARGUMENT;
}
else
{
*(arg - 1) = '\0';
/* Strip double quotes from string argument */
if (*arg == '"')
{
arg++;
*(arg + (strlen(arg) - 1)) = '\0';
}
/* Check keyword */
if (!strcmp(keyword, "BEGIN"))
{
/* Begin new block */
status = parse_block(fd, tl, search_keyword(arg,
&block_keywords[0]), mode);
}
else
{
if (!strcmp(keyword, "END"))
{
/* End current block */
if (search_keyword(arg, &block_keywords[0]) !=
block_type)
{
status = EDITOR_ERROR_END_WRONG_BLOCK;
}
else
{
status = EDITOR_STATUS_EOB;
}
}
else
{
/* Parse item */
status = parse_item(tl, block_type, keyword, arg,
mode);
}
}
}
break;
case TEXT_FILE_STATUS_EOF:
status = EDITOR_STATUS_EOF;
break;
case TEXT_FILE_ERROR_LINE_TOO_LONG:
status = EDITOR_ERROR_LINE_TOO_LONG;
break;
default:
status = EDITOR_ERROR_READ_FAILED;
break;
}
}
while (status == EDITOR_STATUS_NORMAL);
if (status != EDITOR_STATUS_EOB)
{
/* Free incomplete block */
switch (block_type)
{
case BLOCK_TYPE_FONT:
free_template_font(new_font);
break;
case BLOCK_TYPE_TEMPLATE:
free_template(tl, new_template);
break;
case BLOCK_TYPE_TEXTLIST:
free_template_text_list(new_template);
break;
}
}
else
{
if (block_type == BLOCK_TYPE_FONT)
{
AddTail((struct List *) & tl->tl_Fonts,
(struct Node *) & new_font->tf_MinNode);
}
else
{
if (block_type == BLOCK_TYPE_TEMPLATE)
{
struct Template *tp;
struct Box *box;
struct BorderData *bd;
struct TextData *td;
struct GadgetData *gd;
/* Init new template */
tp = new_template;
box = &tp->tp_Box;
tp->tp_Node.ln_Name = &tp->tp_TemplateName[0];
switch (TEMPLATE_GROUP(tp))
{
case TEMPLATE_GROUP_BORDER:
/* Init border template */
bd = &tp->tp_Data.tp_BorderData;
bd->bd_LeftEdge = box->bo_X1;
bd->bd_TopEdge = box->bo_Y1;
bd->bd_Width = box->bo_X2 - box->bo_X1 + 1;
bd->bd_Height = box->bo_Y2 - box->bo_Y1 + 1;
(bd + 1)->bd_Type = INTUISUP_DATA_END;
break;
case TEMPLATE_GROUP_TEXT:
/* Init text template */
td = &tp->tp_Data.tp_TextData;
td->td_LeftEdge = box->bo_X1;
td->td_TopEdge = box->bo_Y1;
(td + 1)->td_Type = INTUISUP_DATA_END;
break;
case TEMPLATE_GROUP_GADGET:
/* Init gadget template */
gd = &tp->tp_Data.tp_GadgetData;
gd->gd_LeftEdge = box->bo_X1;
gd->gd_TopEdge = box->bo_Y1;
gd->gd_Width = box->bo_X2 - box->bo_X1 + 1;
gd->gd_Height = box->bo_Y2 - box->bo_Y1 + 1;
(gd + 1)->gd_Type = INTUISUP_DATA_END;
break;
}
add_template_to_list(tl, tp, FALSE);
}
}
status = EDITOR_STATUS_NORMAL;
}
}
return (status);
}
/* Parse block of load file */
STATIC SHORT
parse_item(struct TemplateList * tl, USHORT block_type, BYTE * keyword,
BYTE * arg, USHORT mode)
{
struct TextAttr *ta;
struct Template *tp;
struct Box *box;
struct BorderData *bd;
struct TextData *td;
struct GadgetData *gd;
SHORT status = EDITOR_STATUS_NORMAL;
switch (block_type)
{
case BLOCK_TYPE_HEADER:
/* Read header data */
if (mode == LOAD_MODE_NORMAL)
{
struct NewWindow *nwin = &project_new_window;
switch (search_keyword(keyword, &header_keywords[0]))
{
case HEADER_ITEM_TYPE_LEFTEDGE:
nwin->LeftEdge = atol(arg);
break;
case HEADER_ITEM_TYPE_TOPEDGE:
nwin->TopEdge = atol(arg);
break;
case HEADER_ITEM_TYPE_WIDTH:
nwin->Width = atol(arg);
break;
case HEADER_ITEM_TYPE_HEIGHT:
nwin->Height = atol(arg);
break;
case HEADER_ITEM_TYPE_MINW:
nwin->MinWidth = atol(arg);
break;
case HEADER_ITEM_TYPE_MINH:
nwin->MinHeight = atol(arg);
break;
case HEADER_ITEM_TYPE_MAXW:
nwin->MaxWidth = atol(arg);
break;
case HEADER_ITEM_TYPE_MAXH:
nwin->MaxHeight = atol(arg);
break;
case HEADER_ITEM_TYPE_DETAILPEN:
nwin->DetailPen = atol(arg);
break;
case HEADER_ITEM_TYPE_BLOCKPEN:
nwin->BlockPen = atol(arg);
break;
case HEADER_ITEM_TYPE_FLAGS:
tl->tl_Flags = atol(arg);
break;
case HEADER_ITEM_TYPE_WINFLAGS:
nwin->Flags = atol(arg);
break;
case HEADER_ITEM_TYPE_IDCMPFLAGS:
nwin->IDCMPFlags = atol(arg);
break;
case HEADER_ITEM_TYPE_ID:
strcpy(&tl->tl_ProjectID[0], arg);
break;
case HEADER_ITEM_TYPE_NAME:
strcpy(tl->tl_ProjectWinName, arg);
break;
}
}
break;
case BLOCK_TYPE_FONT:
/* Read template font data */
ta = &new_font->tf_TextAttr;
switch (search_keyword(keyword, &font_keywords[0]))
{
case FONT_ITEM_TYPE_NAME:
status = duplicate_string(arg, (BYTE **) & ta->ta_Name);
break;
case FONT_ITEM_TYPE_YSIZE:
ta->ta_YSize = atol(arg);
break;
}
break;
case BLOCK_TYPE_TEMPLATE:
/* Read template data */
tp = new_template;
switch (search_keyword(keyword, &template_keywords[0]))
{
case TEMPLATE_ITEM_TYPE_NAME:
strcpy(&tp->tp_TemplateName[0], arg);
break;
case TEMPLATE_ITEM_TYPE_TYPE:
tp->tp_Type = atol(arg);
break;
case TEMPLATE_ITEM_TYPE_FLAGS:
tp->tp_Flags = atol(arg);
break;
}
break;
case BLOCK_TYPE_BOX:
/* Read box data */
box = &new_template->tp_Box;
switch (search_keyword(keyword, &box_keywords[0]))
{
case BOX_ITEM_TYPE_X1:
box->bo_X1 = atol(arg);
break;
case BOX_ITEM_TYPE_Y1:
box->bo_Y1 = atol(arg);
break;
case BOX_ITEM_TYPE_X2:
box->bo_X2 = atol(arg);
break;
case BOX_ITEM_TYPE_Y2:
box->bo_Y2 = atol(arg);
break;
}
break;
case BLOCK_TYPE_TEXTLIST:
/* Read text list */
switch (search_keyword(keyword, &textlist_keywords[0]))
{
case TEXTLIST_ITEM_TYPE_TEXT:
status = add_template_text_list_entry(new_template, arg);
break;
}
break;
case BLOCK_TYPE_BORDERDATA:
/* Read border data */
bd = &new_template->tp_Data.tp_BorderData;
switch (search_keyword(keyword, &borderdata_keywords[0]))
{
case BORDERDATA_ITEM_TYPE_TYPE:
bd->bd_Type = atol(arg);
break;
}
break;
case BLOCK_TYPE_TEXTDATA:
/* Read text data */
td = &new_template->tp_Data.tp_TextData;
switch (search_keyword(keyword, &textdata_keywords[0]))
{
case TEXTDATA_ITEM_TYPE_TYPE:
td->td_Type = atol(arg);
break;
case TEXTDATA_ITEM_TYPE_FLAGS:
td->td_Flags = atol(arg);
break;
case TEXTDATA_ITEM_TYPE_TEXT:
if (td->td_Type == TEXT_DATA_TYPE_TEXT)
{
status = duplicate_string(arg, &td->td_Text);
}
else
{
td->td_Text = (BYTE *) atol(arg);
}
break;
case TEXTDATA_ITEM_TYPE_TEXTATTR:
if (!(td->td_TextAttr = open_template_font_by_num(tl,
(USHORT) atol(arg))))
{
status = EDITOR_ERROR_INVALID_FONT;
}
break;
}
break;
case BLOCK_TYPE_GADGETDATA:
/* Read gadget data */
tp = new_template;
gd = &tp->tp_Data.tp_GadgetData;
switch (search_keyword(keyword, &gadgetdata_keywords[0]))
{
case GADGETDATA_ITEM_TYPE_TYPE:
gd->gd_Type = atol(arg);
break;
case GADGETDATA_ITEM_TYPE_FLAGS:
gd->gd_Flags = atol(arg);
break;
case GADGETDATA_ITEM_TYPE_TEXT:
if (*arg == '0')
{
gd->gd_Text = NULL;
}
else
{
status = duplicate_string(arg, &gd->gd_Text);
}
break;
case GADGETDATA_ITEM_TYPE_TEXTATTR:
if (!(gd->gd_TextAttr = open_template_font_by_num(tl,
(USHORT) atol(arg))))
{
status = EDITOR_ERROR_INVALID_FONT;
}
break;
case GADGETDATA_ITEM_TYPE_SPECIAL1:
gd->gd_SpecialData.gd_Data.gd_Data1 = atol(arg);
break;
case GADGETDATA_ITEM_TYPE_SPECIAL2:
gd->gd_SpecialData.gd_Data.gd_Data2 = atol(arg);
break;
case GADGETDATA_ITEM_TYPE_SPECIAL3:
switch (gd->gd_Type)
{
case GADGET_DATA_TYPE_BUTTON:
case GADGET_DATA_TYPE_CHECK:
case GADGET_DATA_TYPE_INTEGER:
case GADGET_DATA_TYPE_SLIDER:
case GADGET_DATA_TYPE_SCROLLER:
case GADGET_DATA_TYPE_COUNT:
case GADGET_DATA_TYPE_PALETTE:
gd->gd_SpecialData.gd_Data.gd_Data3 = (VOID *) atol(arg);
break;
case GADGET_DATA_TYPE_STRING:
status = duplicate_string(arg,
(BYTE **) & gd->gd_SpecialData.gd_Data.gd_Data3);
break;
case GADGET_DATA_TYPE_MX:
case GADGET_DATA_TYPE_CYCLE:
status = build_template_text_array(tp);
break;
case GADGET_DATA_TYPE_LISTVIEW:
gd->gd_SpecialData.gd_ListViewData.gd_ListViewList = &tp->tp_TextList;
break;
}
break;
}
break;
}
return (status);
}
/* Search keyword in given list */
STATIC USHORT
search_keyword(BYTE * keyword, BYTE ** keyword_list)
{
BYTE *word;
USHORT count = 0, num = 0;
while (word = *keyword_list++)
{
count++;
if (!strcmp(word, keyword))
{
num = count;
break;
}
}
return (num);
}